#ifndef __LOG1_H__
#define __LOG1_H__

#include <iostream>
#include <sstream>
#include <fstream>
#include <cstring>
#include <stdio.h>
#include <stack>

// The following trick uses token pasting to avoid declaring
// an enum in one place, and then re-typing the enums to convert
// them to strings (in Log::ToString).  I found it at:
//
// http://blogs.msdn.com/b/vcblog/archive/2008/04/30/enums-macros-unicode-and-token-pasting.aspx
//
// The file Log_Details.inc declares the enums and is included twice.
// It uses a macro MY_ENUM, which acts differently depending on whether
// we are declaring an enum (here), or turning it into a string
// (in Log::ToString)
//

#define MY_ENUM(x) LOG_DETAIL_##x,
enum log_detail_t {
#include "Log_Details.inc"
};
#undef MY_ENUM

#define MY_ENUM(x) LOG_DETAIL_BIT_##x = 1 << LOG_DETAIL_##x,
enum log_detail_bit_t {
#include "Log_Details.inc"
};
#undef MY_ENUM

// The rest of this class is adapted from
//
// http://www.drdobbs.com/cpp/logging-in-c/201804215
//
// It works as follows: Place this in your code:
// LOG(detail flag) << "message 1 " << " message 2 " << etc << ... ;
// The LOG macro invokes an if, whose else-clause calls Log::Get(),
// which returns an ostringstream.  This ostringstream accumulates
// one line of log output.  When we reach the final semicolon,
// the Log() object created by the macro gets destroyed
// (it's local to the else-clause).  The destructor method then
// outputs the ostringstream to the current log stream.
// Clever, efficient, and nonintrusive.
//

#define LOG(detail) \
    if (!((int)LOG_DETAIL_BIT_##detail & Log::active_details)) ;       \
    else Log().Get(LOG_DETAIL_##detail)

// Cool, eh?

#define LOG_ENTER(detail, name) Log::Entering(LOG_DETAIL_##detail, name)
#define LOG_EXIT()              Log::Exiting()

#define LOG_SHOW_TIME_STAMP(flag)  Log::show_time_stamp = flag
#define LOG_SHOW_DETAIL_NAME(flag) Log::show_log_detail = flag

#define LOG_SET_STREAM(stream)     Log::set_output_stream(stream)

#define LOG_CLEAR_DETAILS()        Log::active_details = 1
#define LOG_ACTIVATE_DETAIL_BIT(b) Log::active_details |= b
#define LOG_ACTIVATE_DETAIL(d)     LOG_ACTIVATE_DETAIL_BIT((int)LOG_DETAIL_BIT_##d)
#define LOG_ACTIVATE_DETAIL_NAME(n) LOG_ACTIVATE_DETAIL_BIT(Log::BitFromDetail(Log::FromString(n)))


class Log
{
public:
    Log();
    virtual ~Log();
    std::ostringstream& Get(log_detail_t detail = (log_detail_t)1);

    std::string NowTime();

    // Static methods in the original DrDobbs code
    static std::string ToString(log_detail_t detail);
    static log_detail_t FromString(const std::string& detail);
    static int BitFromDetail(log_detail_t detail);

    // I added these static methods to modify the log stream,
    // and to record entering and exiting a function.
    static void set_output_stream(std::ostream& stream);
    static void set_stream_file(const std::string& filename);
    static void Entering(log_detail_t detail, const std::string& function_name);
    static void Exiting();

    // static data members:
    static std::ostream *output;
    static std::ofstream output_fstream;
    static bool outputting_to_file;

    // added these to track the names of functions recorded
    //  through Entering() and Exiting()
    static std::stack<std::string> function_names;
    static std::stack<int>         function_details;
    // bit-string which records the active detail flags.
    static int active_details;
    // show time stamp?
    static bool show_time_stamp;
    // show log detail?
    static bool show_log_detail;

protected:
    std::ostringstream os;
private:
    Log(const Log&);
    Log& operator =(const Log&);
};


#endif //__LOG_H__
